#include "drivers/chip/gpio.h"
#include "drivers/chip/irq.h"

#include "remap_stm32f1.h"

#include "stm32f1xx.h"
#include "stm32f1xx_hal_rcc.h"
#include "stm32f1xx_hal_gpio.h"

#define SYS_LOG_DOMAIN "hal"
#include "sys_log.h"

#define DEV (id2dev[pin / 16])

static GPIO_TypeDef *const id2dev[] = {
    GPIOA,
    GPIOB,
    GPIOC,
    GPIOD,
    GPIOE,
    GPIOF,
    GPIOG,
};

void drv_gpio_enable_all(void)
{
    SET_BIT(RCC->APB2ENR, RCC_APB2ENR_AFIOEN |
                              RCC_APB2ENR_IOPAEN |
                              RCC_APB2ENR_IOPBEN |
                              RCC_APB2ENR_IOPCEN |
                              RCC_APB2ENR_IOPDEN |
                              RCC_APB2ENR_IOPEEN);
}

void drv_gpio_disable_all(void)
{
    CLEAR_BIT(RCC->APB2ENR, RCC_APB2ENR_AFIOEN |
                                RCC_APB2ENR_IOPAEN |
                                RCC_APB2ENR_IOPBEN |
                                RCC_APB2ENR_IOPCEN |
                                RCC_APB2ENR_IOPDEN |
                                RCC_APB2ENR_IOPEEN);
}

int drv_gpio_pin_configure(uint8_t pin, gpio_dir_t dir, gpio_pud_t pull)
{
    if (pin >= 144)
    {
        return -1;
    }

    GPIO_TypeDef *gpio = DEV;
    int MODE;
    int CNF;

    switch (dir)
    {
    case _GPIO_DIR_OUT:
    case _GPIO_DIR_OPEN_DRAIN:
        MODE = 3;
        break;
    case _GPIO_DIR_IN:
    case _GPIO_DIR_ANALOG:
    default:
        MODE = 0;
        break;
    }

    switch (pull)
    {
    case _GPIO_PUD_PULL_UP:
        CNF = 2;
        drv_gpio_pin_write(pin, 1);
        break;
    case _GPIO_PUD_PULL_DOWN:
        drv_gpio_pin_write(pin, 0);
        CNF = 2;
        break;
    case _GPIO_PUD_NONE:
    default:
        CNF = 1;
        break;
    }

    pin %= 16;
    int irq_nest = drv_irq_disable();
    writebits(&((__IO uint32_t *)&gpio->CRL)[pin / 8], pin % 8 * 4, 4, CNF << 2 | MODE);
    drv_irq_enable(irq_nest);
    return 0;
}

int drv_gpio_pin_read(uint8_t pin)
{
    if (pin >= 144)
    {
        return -1;
    }

    GPIO_TypeDef *gpio = DEV;
    pin %= 16;
    return (gpio->IDR >> pin & 1);
}

void drv_gpio_pin_write(uint8_t pin, int value)
{
    if (pin >= 144)
    {
        return;
    }

    GPIO_TypeDef *gpio = DEV;
    pin %= 16;
    if (value)
    {
        gpio->BSRR = 1u << pin;
    }
    else
    {
        gpio->BSRR = 1u << (pin + 16);
    }
}

void drv_gpio_exti_callback_enable(uint8_t pin, gpio_exti_edge_t polarity, gpio_exti_cb_fn cb)
{
    if (pin >= 144)
    {
        return;
    }

    SYS_LOG("");
}

void drv_gpio_exti_callback_disable(uint8_t pin)
{
    if (pin >= 144)
    {
        return;
    }

    SYS_LOG("");
}

void drv_gpio_exti_set_priority(uint8_t pin, int priority)
{
    if (pin >= 144)
    {
        return;
    }

    SYS_LOG("");
}

bool drv_gpio_exti_is_pin_state(uint8_t pin)
{
    return false;
}
